package org.ainlolcat.ainscience.column;

import org.ainlolcat.Variable;
import org.ainlolcat.ainscience.column.threshold.Threshold;
import org.ainlolcat.ainscience.matrix.MatrixFileParser;
import org.ainlolcat.ainscience.tools;

import java.util.*;

/**
 * @author ainlolcat
 *         Date: 5/4/13
 */
public abstract class AbstractColumn implements Column {
    protected String name;
    protected Integer type;
    protected String axis;
    protected List values = new ArrayList();
    protected double[] numericRep;
    protected Collection<Threshold> thresholds = new ArrayList<Threshold>();

    protected void clearState(){
        numericRep = null;
    }

    public Collection<Threshold> getThresholds() {
        return thresholds;
    }

    public void setThresholds(Collection<Threshold> thresholds) {
        this.thresholds = thresholds;
    }

    public int size(){
        return values.size();
    }

    //add value operation
    public void addValues(List values){
        clearState();
        this.values.addAll(values);
    }

    public void addValue(String value){
        clearState();
        this.values.add(value);
    }

    //set value operation
    public void setValues(List values){
        clearState();
        this.values = values;
    }

    public boolean setValueAt(int rowNum, Object value){
        if (values==null) return false;
        if (rowNum>=size()) return false;
        clearState();
        values.set(rowNum, value);
        return true;
    }

    private Map<Integer, Variable> varCache = new WeakHashMap<Integer, Variable>();
    public Variable getCanonicalValue(int num) {
        Variable variable = varCache.get(num);
        if (variable != null)
            return variable;
        variable = getCanonicalValueInternal(num);
        varCache.put(num, variable);
        return variable;
    }

    private Variable getCanonicalValueInternal(int num) {
        //TODO make MATRIX_DELIMETER_TYPE_* same as Variable.TYPE_*
        if (type.equals( MatrixFileParser.MATRIX_DELIMETER_TYPE_DOUBLE)){
            return new Variable(getValue(num),Variable.TYPE_DOUBLE);
        }
        if (type.equals( MatrixFileParser.MATRIX_DELIMETER_TYPE_INTEGER)){
            return new Variable(getValue(num),Variable.TYPE_INTEGER);
        }
        if (type.equals( MatrixFileParser.MATRIX_DELIMETER_TYPE_STRING)){
            return new Variable(getValue(num),Variable.TYPE_STRING);
        }
        if (type.equals( MatrixFileParser.MATRIX_DELIMETER_TYPE_BOOLEAN)){
            return new Variable(getValue(num),Variable.TYPE_BOOLEAN);
        }
        return null;
    }

    public Object getValue(int num){
        return (num >= values.size() || num < 0) ? null : values.get(num);
    }

    //remove value operation
    public boolean removeAt(int num){
        if (num<values.size()){
            values.remove(num);
            return true;
        }
        else {
            return false;
        }
    }
    public void removeLast(){
        if (values.size()>0){
            values.remove(values.size()-1);
        }
    }

    //name operations
    public void setName(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    //type operations
    public Integer getType(){
        return type;
    }

    public void setType(Integer type) {
        if (dataTransform(this.type, type))
            this.type = type;
    }

    protected boolean dataTransform(Integer prevType, Integer nextType){
        if (prevType==null) prevType = new Integer(MatrixFileParser.MATRIX_DELIMETER_TYPE_STRING);
        if (prevType.equals(nextType)) return true;

        if (prevType.equals(MatrixFileParser.MATRIX_DELIMETER_TYPE_STRING)){
            if (nextType.equals(MatrixFileParser.MATRIX_DELIMETER_TYPE_DOUBLE)){
                List newvalues = new ArrayList();
                for (int i=0;i<values.size();i++){
                    newvalues.add(tools.parseDouble(values.get(i)));
                }
                if (newvalues.size() == values.size()){
                    values = newvalues;
                    return true;
                }
            }
            if (nextType.equals(MatrixFileParser.MATRIX_DELIMETER_TYPE_INTEGER)){
                List newvalues = new ArrayList();
                for (int i=0;i<values.size();i++){
                    newvalues.add(tools.parseInteger((String) values.get(i)));
                }
                if (newvalues.size() == values.size()){
                    values = newvalues;
                    return true;
                }
            }
            if (nextType.equals(MatrixFileParser.MATRIX_DELIMETER_TYPE_BOOLEAN)){
                List newvalues = new ArrayList();
                for (int i=0;i<values.size();i++){
                    newvalues.add(tools.parseBoolean((String) values.get(i)));
                }
                if (newvalues.size() == values.size()){
                    values = newvalues;
                    return true;
                }
            }
        }
        return false;
    }

    //axis operation
    public String getAxis(){
        return axis;
    }

    public  void setAxis(String axis){
        this.axis = axis;
    }

    public boolean isNumeric() {
        if (MatrixFileParser.MATRIX_DELIMETER_TYPE_DOUBLE.equals(type)||
                MatrixFileParser.MATRIX_DELIMETER_TYPE_INTEGER.equals(type)){
            return true;
        }
        return false;
    }

    public double[] getDoubleRepresentation() {
        if (numericRep!=null) return numericRep;
        if (isNumeric()){
            double[] answer = new double[size()];
            for (int i=0;i<size();i++){
                answer[i] = tools.parseDouble(getValue(i).toString());
            }
            numericRep = answer;
            return  numericRep;
        }
        return null;
    }

    public int findIndex(Object val) {
        for (int i = 0;i<values.size();i++){
            Object value = values.get(i);
            if (value.equals(val)) return i;
        }
        return -1;
    }

    public abstract Column clone();
}
